<?php
/**
 * @link http://www.tintsoft.com/
 * @copyright Copyright (c) 2013 TintSoft LLC
 * @license http://www.tintsoft.com/license/
 */
namespace Leaps\Stdlib;
use Closure;
/**
 * IOC容器类
 *
 * @author Tongle Xu <xutongle@gmail.com>
 * @since 4.0
 */
class IoC
{
    /**
     * 注册依赖项。
     *
     * @var array
     */
    public static $registry = array ();

    /**
     * 处理单例实例
     *
     * @var array
     */
    public static $singletons = array ();

    /**
     * 注册一个对象及其解析器。
     *
     * @param string $name
     * @param mixed $resolver
     * @param bool $singleton
     * @return void
     */
    public static function register($name, $resolver = null, $singleton = false)
    {
        if ( is_null ( $resolver ) ) $resolver = $name;
        static::$registry [$name] = compact ( 'resolver', 'singleton' );
    }

    /**
     * 判断一个对象是否已经注册到容器。
     *
     * @param string $name
     * @return bool
     */
    public static function registered($name)
    {
        return array_key_exists ( $name, static::$registry );
    }

    /**
     * 注册一个单例对象
     *
     * @param string $name
     * @param Closure $resolver
     * @return void
     */
    public static function singleton($name, $resolver = null)
    {
        static::register ( $name, $resolver, true );
    }

    /**
     * 注册一个现有的实例作为一个单例。 <code> // Register an instance as a singleton in the
     * container IoC::instance('mailer', new Mailer); </code>
     *
     * @param string $name
     * @param mixed $instance
     * @return void
     */
    public static function instance($name, $instance)
    {
        static::$singletons [$name] = $instance;
    }

    /**
     * 分解一个给定类型的一个实例。
     * <code> // Get an instance of the "mailer" objectregistered in the container
     * $mailer = IoC::resolve('mailer'); // Get an
     * instance of the "mailer" object and pass parameters to the resolver
     * $mailer = IoC::resolve('mailer', array('test')); </code>
     *
     * @param string $type
     * @param array $parameters
     * @return mixed
     */
    public static function resolve($type, $parameters = array())
    {
        if ( isset ( static::$singletons [$type] ) ) {
            return static::$singletons [$type];
        }
        if ( ! isset ( static::$registry [$type] ) ) {
            $concrete = $type;
        } else {
            $concrete = array_get ( static::$registry [$type], 'resolver', $type );
        }
        if ( $concrete == $type or $concrete instanceof Closure ) {
            $object = static::build ( $concrete, $parameters );
        } else {
            $object = static::resolve ( $concrete );
        }
        if ( isset ( static::$registry [$type] ['singleton'] ) && static::$registry [$type] ['singleton'] === true ) {
            static::$singletons [$type] = $object;
        }
        Event::fire ( 'leaps.resolving', array (
                $type,
                $object
        ) );
        return $object;
    }

    /**
     * 实例化特定类型的一个实例。
     *
     * @param string $type
     * @param array $parameters
     * @return mixed
     */
    protected static function build($type, $parameters = array())
    {
        if ( $type instanceof Closure ) {
            return call_user_func_array ( $type, $parameters );
        }
        $reflector = new \ReflectionClass ( $type );
        if ( ! $reflector->isInstantiable () ) {
            throw new \Exception ( "Resolution target [$type] is not instantiable." );
        }
        $constructor = $reflector->getConstructor ();
        if ( is_null ( $constructor ) ) {
            return new $type ();
        }
        $dependencies = static::dependencies ( $constructor->getParameters (), $parameters );
        return $reflector->newInstanceArgs ( $dependencies );
    }

    /**
     * 从ReflectionParameters解决所有的依赖关系。
     *
     * @param array $parameters
     * @param array $arguments that might have been passed into our resolve
     * @return array
     */
    protected static function dependencies($parameters, $arguments)
    {
        $dependencies = array ();

        foreach ( $parameters as $parameter ) {
            $dependency = $parameter->getClass ();
            if ( count ( $arguments ) > 0 ) {
                $dependencies [] = array_shift ( $arguments );
            } else if ( is_null ( $dependency ) ) {
                $dependency [] = static::resolveNonClass ( $parameter );
            } else {
                $dependencies [] = static::resolve ( $dependency->name );
            }
        }
        return ( array ) $dependencies;
    }

    /**
     * Resolves optional parameters for our dependency injection pretty much
     * took backport straight from L4's Illuminate\Container
     *
     * @param ReflectionParameter
     * @return default value
     */
    protected static function resolveNonClass($parameter)
    {
        if ( $parameter->isDefaultValueAvailable () ) {
            return $parameter->getDefaultValue ();
        } else {
            throw new \Exception ( "Unresolvable dependency resolving [$parameter]." );
        }
    }
}